package com.xinz.serialization;

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Hashtable;

import com.xinz.protocal.config.ShareConfig;

/**
 * 格式： 描述区域 结构
 * Object{
 * 		
 * class serialer{
 * 			
 * 			}
 * 		
 * 	fields{
 * 			
 * 			{
 * 				size
 * 				{Object,Object,Object...}
 * 			}
 * 		}
 * 		
 *super class serialer{
 * 			
 * 		}
 * 		
 *super fields{
 * 			size
 * 			{Object,Object,Object...}
 * 		}
 * 
 * 
 * }
 * 
 * 
 *
 *
 *  
 *  
 *
 * 
 * 
 */
public class BeanOutputStream extends DataOutputStream{

	Hashtable<Object, Integer> objtable = new Hashtable<Object, Integer>();
	
	int refIndex = 0;
	
	StreamContext context;
	
	public BeanOutputStream(OutputStream out) {
		this(out, new StreamContext());
	}
	
	public BeanOutputStream(OutputStream out, StreamContext context) {
		super(out);
		this.context = context;
	}
	
	public void writeObject(Object obj) throws Exception {
		ObjectAccessor accessor = new ObjectAccessor();
		try {
			accessor.set(obj);
			writeAccessor(accessor);
		}
		catch(Exception e) {
			throw e;
		}
		finally {
			clearBuff();
		}
	}
	
	private void clearBuff() {
		refIndex = 0;
		objtable.clear();
	}

	void writeClass(Class<?> clazz) throws IOException {
		Integer index = context.getClassIndex(clazz);
		if(index == null) {
			context.addClass(clazz);
			writeShort(-1);
			writeUTF(clazz.getName());
		}
		else {
			writeShort(index);
		}
	}
	
	public void writeAccessor(Accessor accessor) throws Exception {
		if(accessor == null) {
			write(StreamContext.HEAD_EMPTY);
			return;
		}
		if(accessor.isNull()) {
			write(StreamContext.HEAD_NULL);
			return;
		}
		Class<?> clazz = accessor.getType();
		Serialer serialer = context.getSerialer(clazz);
		// 判断是否基本类型
		if(serialer != null && serialer.isBase()) {
			write(StreamContext.HEAD_BASE);
			writeShort(serialer.getIndex());
			serialer.write(accessor, this);
			return;
		}
		// 判断是否在buff里
		Integer index = objtable.get(accessor.get());
		if(index != null) {
			write(StreamContext.HEAD_REF);
			writeShort(index);
		}
		else {
			write(StreamContext.HEAD_NEW);
			serialer = createObjectSerialer(serialer, clazz);
			addBuff(accessor.get());
			while(true) {
				serialer.write(accessor, this);
				clazz = clazz.getSuperclass();
				if(clazz == null || clazz == Object.class) {
					clazz = null;
					break;
				}
				serialer = context.getSerialer(clazz);
				serialer = createObjectSerialer(serialer, clazz);
			}
		}
	}
	
	private Serialer createObjectSerialer(Serialer serialer, Class<?> clazz) throws IOException, IllegalAccessException {
		if(serialer == null) {
			ObjectSerialer objectSerialer = new ObjectSerialer(clazz);
			context.addSerialer(objectSerialer);
			writeShort(-1);
			writeClass(clazz);
			writeShort(objectSerialer.fields.length);
			for(int i = 0; i < objectSerialer.fields.length; i++) {
				writeUTF(objectSerialer.fields[i].getName());
			}
			serialer = objectSerialer;
		}
		return serialer;
	}
	
	public void addBuff(Object obj) {
		objtable.put(obj, refIndex++);		
	}
}
